//using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

#if !UNITY_FLASH// no static batching in flash

namespace UnityEngine
{
internal class InternalStaticBatchingUtility {
										  // assume 16bit indices
	const int MaxVerticesInBatch = 64000; // a little bit less than 64K - just in case
	const string CombinedMeshPrefix = "Combined Mesh";

	static public void Combine (UnityEngine.GameObject staticBatchRoot)
	{
		Combine(staticBatchRoot, false);
	}
	
	static public void Combine (UnityEngine.GameObject staticBatchRoot, bool combineOnlyStatic)
	{
		GameObject[] gos = (GameObject[])UnityEngine.Object.FindObjectsOfType(typeof(GameObject));

		List<GameObject> filteredGos = new List<GameObject>();
		foreach (GameObject go in gos)
		{
			if (staticBatchRoot != null)
				if (!go.transform.IsChildOf(staticBatchRoot.transform))
					continue;

			if (combineOnlyStatic && !go.isStaticBatchable )
				continue;

			filteredGos.Add(go);
		}

		gos = filteredGos.ToArray();

		// Inform user about Advanced license only feature
		// real license guard is baked into the native code.
		bool hasProOrAdvancedLicense = Application.HasProLicense() || Application.HasAdvancedLicense();
		if (!hasProOrAdvancedLicense)
		{
			// Display error only if invoked from the user script
			if (staticBatchRoot != null && gos.Length > 0)
				Debug.LogError("Your Unity license is not sufficient for Static Batching.");
		}

		Combine(gos, staticBatchRoot);
	}

	static public void Combine(GameObject[] gos, UnityEngine.GameObject staticBatchRoot)
	{
		Matrix4x4 staticBatchInverseMatrix = Matrix4x4.identity;
		Transform staticBatchRootTransform = null;
		if (staticBatchRoot)
		{
			staticBatchInverseMatrix = staticBatchRoot.transform.worldToLocalMatrix;
			staticBatchRootTransform = staticBatchRoot.transform;
		}

		int batchIndex = 0;
		int verticesInBatch = 0;
		List<MeshSubsetCombineUtility.MeshInstance> meshes = new List<MeshSubsetCombineUtility.MeshInstance>();
		List<MeshSubsetCombineUtility.SubMeshInstance> subsets = new List<MeshSubsetCombineUtility.SubMeshInstance>();
        List<GameObject> subsetGOs = new List<GameObject> ();

	    Array.Sort(gos, new SortGO());
		
		foreach (GameObject go in gos)
		{
			MeshFilter filter = go.GetComponent(typeof(MeshFilter)) as MeshFilter;
			if (filter == null)
				continue;

			// reject if has no mesh
			if (filter.sharedMesh == null)
				continue;

			// reject if mesh not readable
			if (!filter.sharedMesh.canAccess)
				continue;

			// reject if has not renderer or renderer is disabled
			if (filter.renderer == null || !filter.renderer.enabled)
				continue;
			
			// reject if already combined for static batching
			if (filter.renderer.staticBatchIndex != 0)
				continue;

			// check if we have enough space inside the current batch
			if (verticesInBatch + filter.sharedMesh.vertexCount > MaxVerticesInBatch)
			{
				MakeBatch (meshes, subsets, subsetGOs, staticBatchRootTransform, batchIndex++);
				meshes.Clear();
				subsets.Clear();
                subsetGOs.Clear();
				verticesInBatch = 0;
			}
			
			MeshSubsetCombineUtility.MeshInstance instance = new MeshSubsetCombineUtility.MeshInstance ();
		    Mesh instanceMesh = filter.sharedMesh;
            instance.meshInstanceID = instanceMesh.GetInstanceID();
			instance.transform = staticBatchInverseMatrix * filter.transform.localToWorldMatrix;
			instance.lightmapTilingOffset = filter.renderer.lightmapTilingOffset;

			//;;Debug.Log("New static mesh (" + go.name + ")verts: " + instance.mesh.vertexCount + 
			//	", tris: " + instance.mesh.triangles.Length + 
			//	", materials: " + filter.renderer.sharedMaterials.Length +
			//	", subs: " + instance.mesh.subMeshCount
			//	);

			meshes.Add(instance);

			Material[] materials = filter.renderer.sharedMaterials;
            if (materials.Length > instanceMesh.subMeshCount)
			{
                Debug.LogWarning("Mesh has more materials (" + materials.Length + ") than subsets (" + instanceMesh.subMeshCount + ")");
				// extra materials don't have a meaning and it screws the rendering as Unity
				// tries to render with those extra materials.
                Material[] newMats = new Material[instanceMesh.subMeshCount];
                for (int i = 0; i < instanceMesh.subMeshCount; ++i)
					newMats[i] = filter.renderer.sharedMaterials[i];
				filter.renderer.sharedMaterials = newMats;
				materials = newMats;
			}

            for (int m = 0; m < System.Math.Min(materials.Length, instanceMesh.subMeshCount); ++m)
			{
				//;;Debug.Log("   new subset : " + m + ", tris " + instance.mesh.GetTriangles(m).Length);
				MeshSubsetCombineUtility.SubMeshInstance subsetInstance = new MeshSubsetCombineUtility.SubMeshInstance();
			    subsetInstance.meshInstanceID = filter.sharedMesh.GetInstanceID();
				subsetInstance.vertexOffset = verticesInBatch;
				subsetInstance.subMeshIndex = m;
			    subsetInstance.gameObjectInstanceID = go.GetInstanceID();
				subsetInstance.transform = instance.transform;
				subsets.Add(subsetInstance);
                subsetGOs.Add(go);
			}
			verticesInBatch += instanceMesh.vertexCount;
		}

        MakeBatch(meshes, subsets, subsetGOs, staticBatchRootTransform, batchIndex);
	}

	static private void MakeBatch(List<MeshSubsetCombineUtility.MeshInstance> meshes, List<MeshSubsetCombineUtility.SubMeshInstance> subsets,  List<GameObject> subsetGOs, Transform staticBatchRootTransform, int batchIndex)
	{
		if (meshes.Count < 2)
			return;

		MeshSubsetCombineUtility.MeshInstance[] aMeshes = meshes.ToArray();
		MeshSubsetCombineUtility.SubMeshInstance[] aSubsets = subsets.ToArray();
				
		string combinedMeshName = CombinedMeshPrefix;
		combinedMeshName += " (root: " + ((staticBatchRootTransform != null)? staticBatchRootTransform.name: "scene") + ")";
		if (batchIndex > 0)
			combinedMeshName += " " + (batchIndex + 1);
		
		Mesh combinedMesh = StaticBatchingUtility.InternalCombineVertices (aMeshes, combinedMeshName);
		StaticBatchingUtility.InternalCombineIndices(aSubsets, combinedMesh);
		
		int subsetIndex = 0;
        for (int item = 0; item < aSubsets.Length; item++)
        {
            MeshSubsetCombineUtility.SubMeshInstance i = aSubsets[item];
            GameObject go = subsetGOs[item];
            Mesh m = combinedMesh;

            MeshFilter filter = (MeshFilter)go.GetComponent(typeof(MeshFilter));
            filter.sharedMesh = m;

            go.renderer.SetSubsetIndex(i.subMeshIndex, subsetIndex);
            go.renderer.staticBatchRootTransform = staticBatchRootTransform;

            // for some reason if GOs were created dynamically
            // then we need to toggle renderer to avoid caching old geometry
            go.renderer.enabled = false;
            go.renderer.enabled = true;

            subsetIndex++;
        }
	}
	
	internal class SortGO : IComparer
	{
		int IComparer.Compare( object a, object b )
		{
			if (a == b)
				return 0;

		    Renderer aRenderer = GetRenderer(a as GameObject);
		    Renderer bRenderer = GetRenderer(b as GameObject);

		    int compare = GetMaterialId(aRenderer).CompareTo(GetMaterialId(bRenderer));
            if (compare == 0)
                compare = GetLightmapIndex(aRenderer).CompareTo(GetLightmapIndex(bRenderer));
		    return compare;
		}

        static private int GetMaterialId(Renderer renderer)
        {
            if (renderer == null || renderer.sharedMaterial == null)
                return 0;
            return renderer.sharedMaterial.GetInstanceID();
        }

	    static private int GetLightmapIndex(Renderer renderer)
		{
            if (renderer == null)
                return -1;
			return renderer.lightmapIndex;
		}

        static private Renderer GetRenderer(GameObject go)
        {
            if (go == null)
                return null;
            MeshFilter filter = go.GetComponent(typeof(MeshFilter)) as MeshFilter;
            if (filter == null)
                return null;

            return filter.renderer;
        }
	}	
}

} // namespace UnityEngine

#endif
